import turtle as t
import random
import pygame
import threading
import os

# 0. 初始化pygame, 加载背景音乐
def initPyGame():
    pygame.init()
    pygame.mixer.init()
    pygame.mixer.music.load(os.path.split(os.path.realpath(__file__))[
                            0] + r'\asset\bgm.mp3')
    pygame.mixer.music.set_volume(0.1)
    pygame.mixer.music.play(loops=0, start=0.0)

# 1. 创建背景
def BackgroundCreator(width, height):
    mz = t.Screen()
    mz.setup(width=width, height=height)
    mz.bgcolor('black')
    mz.title('迷宫小游戏')
    mz.tracer(0)
    return mz

# 2. 构建地图
def MapCreator(m,n,MaxEcnt):
    #从起始点开始随机行走，走到不能再走为止
    def inborder(x, y):
        return x < n and x >= 0 and y < m and y >= 0
    
    # 记录每个点是否经过过
    visited = [[False for i in range(m)] for j in range(n)]
    # 记录迷宫形状，注意n行,m列的迷宫应该包含2n+1行，2m+1列
    # 因为a条平行的通路之间和两侧一共还有a+1堵墙
    map = [[0 for i in range(2*m+1)] for j in range(2*n+1)]
    dx = [-1, 1, 0, 0]
    dy = [0, 0, -1, 1]
    # 分别是上下左右的位移矢量
    # 起始点位置和终止点位置
    startposx = 0
    startposy = 0
    endposx = m-1
    endposy = n-1
    # px，py是指针当前指向的位置，visited[px][py]对应于map[2px+1][2py+1]
    px = startposx
    py = startposy
    visited[px][py] = True
    visitedpos = [(px, py)]  # 之前走过的所有点构成的列表
    map[2*px+1][2*py+1] = 1
    sum = 1  # 经过点的总数，当达到mn时迷宫就构建好了
    while(sum < m*n):
        walkable = []  # 存储px，py点前后左右四个点中能到达的点对应的i
        for i in range(4):
            # 计算新的点的位置
            tx = px+dx[i]
            ty = py+dy[i]
            if inborder(tx, ty) and not visited[tx][ty]:  # 能到达就将i加入walkable
                walkable.append(i)
        if not walkable or (px == endposx and py == endposy):
            # 如果walkable为空，或已到达终点，则向之前的位置随机传送
            px, py = random.choice(visitedpos)
        else:
            # 随机选择方向行进
            i = random.choice(walkable)
            px += dx[i]
            py += dy[i]
            visited[px][py] = True
            visitedpos.append((px, py))
            # 将到达的点和前后两点之间的墙赋值为1（路）
            map[2*px+1][2*py+1] = 1
            map[2*px+1-dx[i]][2*py+1-dy[i]] = 1
            sum += 1

    # 返回map
    smap = []
    ecnt = 0
    for j in range(2*n+1):
        s = ""
        for i in range(2*m+1):
            r = random.random()
            if map[j][i]:
                if(r >= 0 and r < 0.1):
                    s += 'G'
                elif( r>= 0.1 and r < 0.15 and ecnt<MaxEcnt):
                    s += 'E'
                    ecnt += 1
                else:
                    s += ' '
            else:
                s += '@'
        print(s)
        smap.append(s)
    return [smap, map]

# 3. 创建画笔类
class Pen(t.Turtle):
    def __init__(self):
        super().__init__()
        self.ht()
        self.shape(wall)
        self.speed(0)
        self.penup()
        self.st()
    def DrawMaze(self, level):
        for i in range(len(level)):
            row = level[i]
            for j in range(len(row)):
                screen_x = -240 + 24 * j
                screen_y = 240 - 24 * i
                if(row[j] == '@'):
                    self.goto(screen_x,screen_y)
                    self.stamp()
                elif(row[j] == 'G'):
                    # 调用金币类绘制金币
                    gold = Gold()
                    golds.append(gold)
                    gold.goto(screen_x,screen_y)
                    gold.st()
                elif(row[j] == 'E'):
                    evil = Evil()
                    evils.append(evil)
                    evil.goto(screen_x, screen_y)
                    evil.st()

# 4. 创建玩家类
class Player(t.Turtle):
    def __init__(self, map):
        super().__init__()
        self.ht()
        self.shape(pr)
        self.speed(0)
        self.penup()
        self.goto(-240+24, 240-24)
        self.st()
        self.map = map
    def go_right(self):
        x = self.xcor() + 24
        y = self.ycor()
        if(self.is_wall(x,y) and self.find_evil()):
            self.shape(pr)
            self.goto(x,y)
            self.find_gold()
            if(self.win()):
                pygame.mixer.music.pause()
                pygame.mixer.Sound.play(win)
                showMessage("You Win!!", "Score: " + str(score))
            mz.update()

    def go_left(self):
        x = self.xcor() - 24
        y = self.ycor()
        if(self.is_wall(x, y) and self.find_evil()):
            self.shape(pl)
            self.goto(x, y)
            self.find_gold()
            if(self.win()):
                pygame.mixer.music.pause()
                pygame.mixer.Sound.play(win)
                showMessage("You Win!!", "Score: " + str(score))
            mz.update()

    def go_up(self):
        x = self.xcor()
        y = self.ycor() + 24
        if(self.is_wall(x, y) and self.find_evil()):
            self.goto(x, y)
            self.find_gold()
            if(self.win()):
                pygame.mixer.music.pause()
                pygame.mixer.Sound.play(win)
                showMessage("You Win!!", "Score: " + str(score))
            mz.update()

    def go_down(self):
        x = self.xcor()
        y = self.ycor() - 24
        if(self.is_wall(x, y) and self.find_evil()):
            self.goto(x, y)
            self.find_gold()
            if(self.win()):
                pygame.mixer.music.pause()
                pygame.mixer.Sound.play(win)
                showMessage("You Win!!", "Score: " + str(score))
            mz.update()

    def is_wall(self,x,y):
        i = (int)((x + 240) / 24)
        j = (int)((240 - y) / 24)
        return map[j][i]

    def find_gold(self):
        global score
        for g in golds:
            if(g.distance(player) == 0):
                pygame.mixer.Sound.play(ouch)
                score += 1
                showScoreAndLiveness()
                print(score)
                g.ht()
                golds.remove(g)

    def find_evil(self):
        global score
        global live
        for e in evils:
            if(e.distance(player) == 0):
                live -= 1
                pygame.mixer.Sound.play(hurt)
                showScoreAndLiveness()
            if(live <= 0):
                showMessage("Game over", "Score: " + str(score))
                pygame.mixer.music.pause()
                pygame.mixer.Sound.play(over)
                return False
        return True

    def win(self):
        return self.xcor() == 240 - 24 and self.ycor() == -240 + 24
                
# 创建金币类
class Gold(t.Turtle):
    def __init__(self):
        super().__init__()
        self.ht()
        self.shape(gold)
        self.speed(0)
        self.penup()
        self.st()

# 创建恶魔类
class Evil(t.Turtle, threading.Thread):
    def __init__(self):
        super().__init__()
        self.ht()
        self.shape(evil)
        self.speed(0)
        self.penup()
        self.st()
        self.fx = 'U'
    def move(self):
        self.turn()
        if(self.fx == 'U'):
            x = self.xcor()
            y = self.ycor() + 24
        elif(self.fx == 'D'):
            x = self.xcor()
            y = self.ycor() - 24
        elif(self.fx == 'R'):
            x = self.xcor() + 24
            y = self.ycor()
        elif(self.fx == 'L'):
            x = self.xcor() - 24
            y = self.ycor()
        if (x >= -240+24 and y >= -240+24 and x <= 240-24 and y <= 240-24):
            self.goto(x, y)
        t.ontimer(self.move, random.randint(400, 800))
    def turn(self):
        # 跟随功能
        if(self.distance(player) < 48):
            if(self.xcor() < player.xcor()):
                self.fx = 'R'
            elif(self.xcor() > player.xcor()):
                self.fx = 'L'
            elif(self.ycor() < player.ycor()):
                self.fx = 'U'
            elif(self.ycor() > player.ycor()):
                self.fx = 'D'
        else:
            self.fx = random.choice(['U', 'D', 'R', 'L'])

# 消息提示相关方法
def showMessage(title,subtitle):
    message_pen = t.Turtle()  # 用来写成功消息的画笔
    message_pen.ht()
    message_pen.speed(0)
    message_pen.penup()
    message_pen.goto(-100, -100)
    message_pen.fillcolor('blue')  # 设置填充色
    message_pen.begin_fill()  # 开始填充
    for i in range(4):
        message_pen.fd(200)
        message_pen.left(90)
    message_pen.end_fill()  # 结束填充
    message_pen.goto(0, 20)
    message_pen.color('yellow')
    message_pen.write(title, align='center', font=('Arial', 25, 'bold'))
    message_pen.goto(0, -45)
    message_pen.color('yellow')
    message_pen.write(subtitle, align='center', font=('Arial', 20, 'bold'))

# 绘制分数与生命画板
def showScoreAndLiveness():
    scoreBoard.clear()
    scoreBoard.goto(-120, 260)
    scoreBoard.color('yellow')
    scoreBoard.write("Score: " + str(score) + 
                     "   Live: " +  str(live), align='center', font=('Arial', 20, 'bold'))

if __name__ == '__main__':
    initPyGame()
    mz = BackgroundCreator(700, 700)
    # 加载并注册图片资源
    wall = os.path.split(os.path.realpath(__file__))[
        0] + r'\asset\wall.gif'
    mz.register_shape(wall)
    pl = os.path.split(os.path.realpath(__file__))[
        0] + r'\asset\pl.gif'
    mz.register_shape(pl)
    pr = os.path.split(os.path.realpath(__file__))[
        0] + r'\asset\pr.gif'
    mz.register_shape(pr)
    gold = os.path.split(os.path.realpath(__file__))[
        0] + r'\asset\gold.gif'
    mz.register_shape(gold)
    evil = os.path.split(os.path.realpath(__file__))[
        0] + r'\asset\e.gif'
    mz.register_shape(evil)
    ouch = pygame.mixer.Sound(os.path.split(os.path.realpath(__file__))[
        0] + r'\asset\gold.mp3')
    ouch.set_volume(0.2)
    over = pygame.mixer.Sound(os.path.split(os.path.realpath(__file__))[
        0] + r'\asset\Over.mp3')
    over.set_volume(0.2)
    win = pygame.mixer.Sound(os.path.split(os.path.realpath(__file__))[
        0] + r'\asset\win.mp3')
    win.set_volume(0.2)
    hurt = pygame.mixer.Sound(os.path.split(os.path.realpath(__file__))[
        0] + r'\asset\hurt.ogg')
    hurt.set_volume(0.2)

    # 调用生成地图函数, 生成关卡
    levels = []
    golds = []
    evils = []
    live = 5
    score = 0
    scoreBoard = t.Turtle()
    scoreBoard.ht()
    showScoreAndLiveness()
    level_1, map = MapCreator(10,10,3)
    levels.append(level_1)

    # 调用画笔类绘制地图
    pen = Pen()
    pen.DrawMaze(levels[0])

    # 控制玩家的移动
    player = Player(map)
    mz.listen()
    mz.onkey(player.go_right,'Right')
    mz.onkey(player.go_left, 'Left')
    mz.onkey(player.go_up, 'Up')
    mz.onkey(player.go_down, 'Down')

    # 控制恶魔移动
    for e in evils:
        t.ontimer(e.move, random.randint(400,800))

    mz.tracer(1)
    mz.mainloop()